---
title: "Codemod Package Structure"
sidebarTitle: "Package structure"
---

<Info>
Codemod CLI and workflow features are free for local use. For large-scale refactoring across many repos, the [Codemod app](https://go.codemod.com/app) provides:

- **Visual parameter configuration** - Edit workflow parameters through the UI instead of YAML
- **Centralized state management** - Coordinate migrations with persistent state across repos and teams  
- **Multi-repo orchestration** - Run workflows across your entire codebase with progress tracking
- **Business insights** - Track migration progress with custom metrics and dashboards

</Info>

---

<Tip>
  This is an advanced topic. If you're just getting started, we recommend using [Codemod Studio](/codemod-studio) or [Codemod MCP](/model-context-protocol) to create your Codemod package with the help of AI.
</Tip>

## Package Structure

### Directory Layout

```
my-codemod-package/
├─ codemod.yaml
├─ workflow.yaml
├─ scripts/
└─ rules/
```

The folder—called a **Codemod package**—is the root when you run `npx codemod workflow run -w ./my-codemod-package/`.

<Info>
You can combine types in a single package. The <code>scripts/</code> and <code>rules/</code> folders are conventional, not required—use any paths and reference them from <code>workflow.yaml</code>.
</Info>

A Codemod package is a directory containing your `workflow.yaml` and any scripts, rules, or assets referenced by your workflow.

<Steps>
<Step title="Scaffolding packages">
  ```bash
  npx codemod init
  ```
</Step>

<Step title="Validating workflows">
  ```bash
  npx codemod workflow validate -w workflow.yaml
  ```
</Step>

<Step title="Running workflows">
  <CodeGroup>

  ```bash Running a package
  npx codemod workflow run -w ./my-codemod-package/
  ```

  ```bash Directly running a workflow file
  npx codemod workflow run -w workflow.yaml
  ```

  </CodeGroup>
</Step>
</Steps>

---

## Package Metadata (codemod.yaml)

The `codemod.yaml` file defines your Codemod package's metadata and configuration.

Example codemod.yaml file:

```yaml codemod.yaml
schema_version: "1.0"

name: "my-awesome-codemod"
version: "0.1.0"
description: "Transform legacy patterns to modern syntax"
author: "Your Name <you@example.com>"
license: "MIT"
workflow: "workflow.yaml"
category: "migration"

targets:
  languages: ["typescript"]

keywords: ["upgrade", "breaking-change", "react", "v17-to-v18"]

registry:
  access: "public"
  visibility: "public"
```

<Accordion title="Available codemod.yaml fields">

<ParamField path="schema_version" type="string" required default="1.0">
Codemod workflow schema version.
</ParamField>

<ParamField path="name" type="string" required>
Codemod package name (unique within scope).

**Naming rules:** `/^[a-z0-9-_/]+$/` (lowercase letters, numbers, hyphens, underscores, and `/` for scope separation only)

**Not allowed:** uppercase letters, dots, commas, spaces, or other special characters

Valid examples:
- <code>remove-console-logs</code>
- <code>@scope/remove-console-logs</code> (using `@organization-or-project-scope/name` provides better discoverability in the [Codemod Registry](https://go.codemod.com/registry))
</ParamField>

<ParamField path="version" type="string" required default="0.1.0">
Semantic version of the package.
</ParamField>

<ParamField path="description" type="string" required>
Brief description of what the codemod does.
</ParamField>

<ParamField path="author" type="string" required>
Author name and email, e.g., `Jane Doe <jane@example.com>`.
</ParamField>

<ParamField path="license" type="string" required default="MIT">
License identifier (SPDX), e.g., <code>MIT</code>.
</ParamField>

<ParamField path="workflow" type="string" required default="workflow.yaml">
Relative path to your workflow file.
</ParamField>

<ParamField path="category" type="string" default="migration">
Category for the codemod.
</ParamField>

<ParamField path="targets.languages" type="string[]">
Languages targeted by this codemod (selected language during `codemod init`; editable later).
</ParamField>

<ParamField path="keywords" type="string[]">
Keyword tags are an optional feature helping developers quickly identify the purpose, scope, and relevance of a codemod. They also enable better search, filtering, and reporting when managing many codemods across frameworks and projects.

Example: `keywords: ["react", "v18-to-v19", "migration"]`

<Accordion title="Best practices and conventions">

  - Keep tags concise (1–2 words).
  - Use lowercase for consistency.
  - Don't overload with tags — 2–4 per codemod is ideal.
  - Prioritize **transformation type + framework/library + version (if relevant)**.

  ---

  **1. Transformation Type Tags**

  Consider these categories when describing **why** the codemod exists:

  - **`upgrade`** – helps upgrade code to newer versions (encompasses both breaking changes and feature adoption).
    You may also consider adding **one** of the following tags:
    - **`breaking-change`** – adapts code to framework/library breaking API changes.
    - **`feature-adoption`** – helps adoption of new optional or incremental features.
  - **`security`** – addresses known vulnerabilities or unsafe patterns.
  - **`cross-migration`** – replaces one library/framework with another.
  - **`i18n`** – internationalization migrations or improvements.
  - **`a11y`** – accessibility improvements and compliance.
  - **`standardization`** – unifies patterns, conventions, or APIs across a codebase.
  - **`code-mining`** – identifies, flags, or extracts patterns without transforming. Use if codemod is for *detection-only*.

  **Rule of thumb**: Pick one primary type tag per codemod. Avoid mixing `breaking-change` and `feature-adoption` in the same codemod.

  ---

  **2. Target Version Tags**

  Use these to indicate the **framework/library version** the codemod prepares for.

  - Format: `vX` or `vX-to-vY` (for upgrades).
  - Examples:
      - `v17-to-v18` (React 17 → 18)
      - `v5-to-v6` (React Router 5 → 6)
      - `v16` (Angular 16 breaking changes)

  **Rule of thumb**: If the codemod is version-specific, always tag it.

  ---

  **3. Framework / Library / SDK Tags**

  Always add the **ecosystem name** to improve discoverability.

  - Examples:
      - `react`
      - `nextjs`
      - `nodejs`
      - `angular`
      - `msw`
      - `i18next`

  **Rule of thumb**: Use the official, common name of the framework/library.

  ---

  **4. Example Tag Sets**

  Here are some examples to illustrate how tags combine:

  - **React Root API Upgrade (17 → 18)**
      - Tags: `upgrade`, `breaking-change`, `v17-to-v18`, `react`
  - **Adopt React Hooks**
      - Tags: `upgrade`, `feature-adoption`, `react`
  - **Migrate from Moment.js to Day.js**
      - Tags: `cross-migration`, `momentjs`, `dayjs`
  - **Remove Hardcoded Strings for i18n**
      - Tags: `i18n`, `i18next`
  - **Add ARIA labels for accessibility**
      - Tags: `a11y`, `react`
  - **Detect Insecure crypto API usage**
      - Tags: `security`, `nodejs`, `crypto`

</Accordion>
</ParamField>

<ParamField path="registry.access" type="string" default="public">
Access controls who can run/use the codemod (once they can see it).

- <strong>public</strong>: Anyone can run the codemod.
- <strong>private</strong>: Only the owner can run the codemod.
- <strong>pro</strong>: Only Pro plan users can run the codemod.

<Tip>
Access applies on top of visibility. For example, <code>visibility: public</code> with <code>access: pro</code> shows the package publicly, but only Pro users can run it.
</Tip>
</ParamField>

<ParamField path="registry.visibility" type="string" default="public">
Visibility controls who can see the package in the Registry (search, listings, and UI).

- <strong>public</strong>: Everyone can see the package in the Registry.
- <strong>private</strong>: Only the owner can see the package.
- <strong>org</strong>: Members of the package's organization scope can see the package.
- <strong>user</strong>: Visible only to the publishing user (user-scoped visibility).

<Info>
During scaffolding, the CLI sets <code>public</code>/<code>private</code> based on <code>--private</code>. You can change to any supported value above when publishing.
</Info>
</ParamField>

<ParamField path="capabilities" type="string[]" default="[]">
Array of capabilities required by this codemod for accessing sensitive operations.

**Available capabilities:**
- `fs` - File system access (`fs`, `fs/promises`)
- `fetch` - Network requests (global `fetch`)
- `child_process` - Process spawning (`child_process`)

<Warning>
Only enable capabilities your codemod actually needs. Each capability increases security risk for untrusted codemods.
</Warning>

<Tip>
See <a href="/jssg/security">Security & Capabilities</a> for detailed information about the security model and usage examples.
Expand commentComment on line R224Resolved
</Tip>
</ParamField>

</Accordion>
---

## Workflow File (workflow.yaml)

The `workflow.yaml` file defines your Codemod package's workflow. A workflow is a collection of nodes that are executed in order. A workflow has four top-level keys:

```yaml workflow.yaml
version: "1"
state:
  schema: []
templates: []
nodes: []
```

| Key                       | Required | Purpose                                           |
| ------------------------- | -------- | ------------------------------------------------- |
| `version`                 | ✓        | Declare workflow schema version (default: `"1"`). |
| [`nodes`](#nodes)         | ✓        | Executable DAG.                                   |
| [`state`](#shared-state)  |          | Declares shared-state schema.                     |
| [`templates`](#templates) |          | Re-usable blocks.                                 |

---

## Nodes & Steps

### Nodes

Nodes are execution units in your workflow. They can be automatic or manual, depend on other nodes, and may define strategy (e.g., matrix), trigger, runtime, env, and an ordered list of steps.

```yaml
version: "1"
nodes:
  - id: build
    name: Build
    type: automatic
    steps:
      - name: npm install
        run: npm ci
```

<ParamField path="id" type="string" required>
  Unique within the workflow.
</ParamField>

<ParamField path="name" type="string" required>
  Display name.
</ParamField>

<ParamField path="type" type="string">
  `automatic` (default) or `manual`.
</ParamField>

<ParamField path="depends_on" type="string[]">
  Upstream node IDs. Learn more about cyclic dependencies [here](#cyclic-dependency-example).
</ParamField>

<ParamField path="trigger" type="object">
  `{ type: manual }` → approval gate. Learn more about manual triggers [here](#manual-trigger).
</ParamField>

<ParamField path="strategy" type="object">
  Matrix configuration. Learn more about matrix strategies [here](#matrix-strategy).
</ParamField>

<ParamField path="steps" type="array" required>
  Ordered list of [steps](#steps).
</ParamField>

<ParamField path="env" type="object">
  Environment variables for the node or step.
</ParamField>

### Steps

Steps are the atomic actions inside a node. They run sequentially and each step performs one transformation or action using a specific engine:

- [`js-ast-grep`](#jssg-js-ast-grep-step) - **Primary transformation engine**: JavaScript/TypeScript codemods using ast-grep
- [`ast-grep`](#yaml-ast-grep-step) - Declarative pattern matching with ast-grep YAML rules
- [`run`](#shell-command-step) - Run shell commands (e.g. setup, cleanup, invoking external tools)
- [`ai`](#ai-step) - LLM-powered transformations and tasks (e.g. non-deterministic transformations, code reviews, summarization)
- [`codemod`](#codemod-registry-step) - Run codemods from the Codemod Registry

To use a jssg step:

<Steps>
  <Step title="Create a jssg codemod:">
    Under `scripts/` folder, create a new jssg codemod.
    ```ts scripts/codemod.ts
    export default function transform(root: SgRoot<TSX>, options) {
      // Your transform logic here
    }
    ```

    Learn more about how to write jssg codemods: [jssg documentation](/jssg/quickstart).
  </Step>
  <Step title="Add a jssg step to your workflow:">
    ```yaml workflow.yaml
    steps:
      - name: Run jssg codemod
        js-ast-grep:
          js_file: "scripts/codemod.ts"
          base_path: "."
          language: "typescript"
          include:
            - "**/*.ts"
            - "**/*.tsx"
    ```

    <Accordion title="Available parameters">
    <ParamField path="name" type="string" required>
      Step label.
    </ParamField>

    <ParamField path="if" type="string">
      Conditional expression to gate step execution. Supports `params.x`, `state.x`, and matrix value keys. Operators: `==`, `!=`, `>`, `<`, `&&`, `||`.
      
      ```yaml
      - name: Transform only for specific shard
        if: params.shardingMethod == "directory"
        js-ast-grep:
          js_file: "src/codemod.ts"
          language: "tsx"
      ```
      
      See [Variable resolution](#variable-resolution) for full syntax.
    </ParamField>

    <ParamField path="js-ast-grep.js_file" type="string" required>
      Path to the JS/TS file that implements the codemod.
    </ParamField>

    <ParamField path="js-ast-grep.language" type="string">
      Target language (e.g., `typescript`, `javascript`, etc.).
    </ParamField>

    <ParamField path="js-ast-grep.include" type="string[]">
      Include glob patterns.
    </ParamField>

    <ParamField path="js-ast-grep.exclude" type="string[]">
      Exclude glob patterns.
    </ParamField>

    <ParamField path="js-ast-grep.base_path" type="string">
      Base path for resolving globs.
    </ParamField>

    <ParamField path="js-ast-grep.max_threads" type="number">
      Maximum concurrent threads.
    </ParamField>

    <ParamField path="js-ast-grep.dry_run" type="boolean" default="false">
      Perform a dry run without applying changes.
    </ParamField>
  </Accordion>

  </Step>
</Steps>

#### AI step

Calls an AI agent with a prompt. This is helpful when your workflow requires leveraging LLM intelligence for more complex tasks for capabilities beyond deterministic transformations.

To use a AI step:

<Steps>
  <Step title="Set the environment variables:">
  You can either set the environment variables in your shell or in your environment file.
  ```bash .env
  LLM_API_KEY=YOUR_KEY
  LLM_PROVIDER=openai # or anthropic, azure_openai
  LLM_MODEL=gpt-4o # or the model you want to use
  LLM_BASE_URL=https://api.openai.com/v1 # or the base URL of the provider you want to use
  ```
  </Step>

  <Step title="Add an AI step to your workflow:">
  ```yaml workflow.yaml
  steps:
    - name: Review diffs with AI
      ai:
        prompt: |
          Summarize risky changes and suggest tests.
        model: "gpt-4o"
  ```

  <Accordion title="Available parameters">
    <ParamField path="name" type="string" required>
      Step label.
    </ParamField>

    <ParamField path="if" type="string">
      Conditional expression to gate step execution. Supports `params.x`, `state.x`, and matrix value keys. Operators: `==`, `!=`, `>`, `<`, `&&`, `||`.
      
      ```yaml
      - name: AI review only if enabled
        if: params.autoAIReview
        ai:
          prompt: "Review the changes"
      ```
      
      See [Variable resolution](#variable-resolution) for full syntax.
    </ParamField>

    <ParamField path="ai.prompt" type="string" required>
      Prompt to send to the AI agent.
    </ParamField>

    <ParamField path="ai.model" type="string" default="gpt-4o">
      Model identifier. Overrides `LLM_MODEL` if set.
    </ParamField>

    <ParamField path="ai.system_prompt" type="string">
      System prompt to scope the AI agent's behavior.
    </ParamField>

    <ParamField path="ai.max_steps" type="number" default="30">
      Maximum number of agent steps before stopping.
    </ParamField>

    <ParamField path="ai.llm_protocol" type="string" default="openai">
      LLM provider/protocol. Supported: `openai`, `anthropic`, `azure_openai`.
    </ParamField>

    <ParamField path="ai.endpoint" type="string">
      LLM base URL. If omitted, defaults to the provider's standard endpoint for the selected protocol. You can override with a custom endpoint.
    </ParamField>

    <ParamField path="ai.api_key" type="string">
      API key to access the LLM.
      
      **The AI step requires valid LLM credentials. Provide them via environment variables:**

      ```bash
      LLM_API_KEY=YOUR_KEY
      LLM_PROVIDER=openai
      LLM_MODEL=gpt-4o
      LLM_BASE_URL=https://api.openai.com/v1
      ```

      <Tip>
        LLM_BASE_URL is optional. If omitted, defaults to the provider's standard endpoint for the selected protocol. You can override with a custom endpoint.
      </Tip>
    </ParamField>
  </Accordion>
  </Step>
</Steps>

#### YAML ast-grep step

Executes ast-grep using declarative YAML rules. Use for simple, fast pattern matching when you don't need programmatic logic.

**When to use YAML ast-grep**:
- Simple find/replace transformations
- No conditional logic needed
- Fastest to write for basic patterns

**When to use [jssg](#jssg-js-ast-grep) instead**:
- Need conditional logic or loops
- Complex AST manipulations
- Cross-file coordination

To use a YAML ast-grep step:

<Steps>
  <Step title="Create a YAML ast-grep rules file:">
    Under `rules/` folder, create a new YAML ast-grep rules file.
    ```yaml rules/config.yml
    id: replace-console-log
    language: typescript
    rule:
      any:
        - pattern: console.log($ARG)
        - pattern: console.debug($ARG)
    fix: logger.log($ARG)
    ```

    Learn more about how to write YAML ast-grep rules: [ast-grep YAML reference](https://ast-grep.github.io/reference/yaml.html)
  </Step>
  <Step title="Add a YAML ast-grep step to your workflow:">
    ```yaml workflow.yaml
    steps:
      - name: "Scan {language} files and apply fixes"
        ast-grep:
          config_file: "rules/config.yml"
          base_path: "./src"
          include:
            - "**/*.js"
          exclude:
            - "**/*.test.js"
    ```

    <Accordion title="Available parameters">
      <ParamField path="name" type="string" required>
        Step label.
      </ParamField>

      <ParamField path="if" type="string">
        Conditional expression to gate step execution. Supports `params.x`, `state.x`, and matrix value keys. Operators: `==`, `!=`, `>`, `<`, `&&`, `||`.
        
        ```yaml
        - name: Apply rules only for TypeScript
          if: params.language == "typescript"
          ast-grep:
            config_file: "rules/typescript.yml"
        ```
        
        See [Variable resolution](#variable-resolution) for full syntax.
      </ParamField>

      <ParamField path="ast-grep.config_file" type="string" required>
        Path to the ast-grep configuration file (.yaml).
      </ParamField>

      <ParamField path="ast-grep.include" type="string[]">
        Include glob patterns.
      </ParamField>

      <ParamField path="ast-grep.exclude" type="string[]">
        Exclude glob patterns.
      </ParamField>

      <ParamField path="ast-grep.base_path" type="string">
        Base path for resolving globs.
      </ParamField>

      <ParamField path="ast-grep.max_threads" type="number">
        Maximum concurrent threads.
      </ParamField>
    </Accordion>
  </Step>
</Steps>

#### Codemod Registry step

Runs another codemod by package name (or local path). Use to compose larger migrations by chaining codemods.

To use a Codemod Registry step:

<Steps>
  <Step title="Find or publish a codemod to the Codemod Registry:">
    You can:
    - find and use an existing codemod in [Codemod Registry](https://go.codemod.com/registry), or
    - [publish a Codemod package](/cli/cli-reference#publish-codemod) to Codemod Registry.
  </Step>
  <Step title="Add a Codemod Registry step to your workflow:">
    ```yaml workflow.yaml
    steps:
      - name: Run registry codemod
        codemod:
          source: "@scope/package"
          args: ["--flag", "value"]
          env:
            NODE_ENV: production
            working_dir: "."
    ```

    <Accordion title="Available parameters">
      <ParamField path="name" type="string" required>
        Step label.
      </ParamField>

      <ParamField path="if" type="string">
        Conditional expression to gate step execution. Supports `params.x`, `state.x`, and matrix value keys. Operators: `==`, `!=`, `>`, `<`, `&&`, `||`.
        
        ```yaml
        - name: Run only for React 18
          if: params.reactVersion == "18"
          codemod:
            source: "@codemod/react-18-19"
        ```
        
        See [Variable resolution](#variable-resolution) for full syntax.
      </ParamField>

      <ParamField path="codemod.source" type="string" required>
        Codemod source (registry package or local path).

        <Tip>
          <b>Version pinning</b>: specify an exact version in <code>source</code> (e.g., <code>@scope/pkg@1.2.3</code>) for reproducible runs. If you omit the version, the latest published version is used.
        </Tip>
      </ParamField>

      <ParamField path="codemod.args" type="string[]">
        CLI arguments passed to the codemod.
      </ParamField>

      <ParamField path="codemod.env" type="object">
        Environment variables used during execution.
      </ParamField>

      <ParamField path="codemod.working_dir" type="string">
        Working directory for execution.
      </ParamField>

      <ParamField path="env" type="object">
        Step-level environment variables. For `codemod` steps, these are forwarded to the invoked workflow as parameters with an `env_` prefix (e.g., `FOO` → `env_FOO`). They are not applied to the OS environment of the nested workflow.
      </ParamField>

      **Aggregate multiple codemods:**

      <Info>
        You can <b>aggregate multiple codemods</b> by adding more than one <code>codemod</code> step in the same node. A Codemod package may itself orchestrate other codemods, enabling nested compositions for larger upgrades.
      </Info>

    ```yaml workflow.yaml
      steps:
        - name: Convert `createRequireFromPath` to `createRequire`
          codemod:
            source: "@nodejs/create-require-from-path"
        - name: Handle DEP0147 (`fs.rmdir` to `fs.rm`)
          codemod:
            source: "@nodejs/rmdir"
      ```

      <Info>
        - Steps in a node **run sequentially** from top to bottom.
        - Use [`depends_on`](#param-depends-on) or a matrix [`strategy`](#matrix-strategy) across multiple nodes for gating/parallelism.
        - If a step fails, the node fails and subsequent steps in that node are skipped.
      </Info>
    </Accordion>
  </Step>
</Steps>

#### Shell command step
Runs shell commands on the host. Use for setup/cleanup, invoking external tools, or glue logic between transformations.

```yaml workflow.yaml
steps:
  - name: Install dependencies
    run: |
      npm install
      npm run build
```

<Accordion title="Available parameters">
  <ParamField path="name" type="string" required>
    Step label.
  </ParamField>

  <ParamField path="if" type="string">
    Conditional expression to gate step execution. Supports `params.x`, `state.x`, and matrix value keys. Operators: `==`, `!=`, `>`, `<`, `&&`, `||`.
    
    ```yaml
    - name: Run only in production
      if: params.environment == "production"
      run: npm run deploy
    ```
    
    See [Variable resolution](#variable-resolution) for full syntax.
    </ParamField>

  <ParamField path="run" type="string" required>
    Inline shell command to execute.
    </ParamField>

  <ParamField path="env" type="object">
    Step-level environment variables applied to the process environment for this `run` step.
    </ParamField>
  </Accordion>

---

## Shared State

State enables workflows to persist data across runs and coordinate work across repositories and teams:

- **Sharding:** Distribute work across teams or repositories  
- **Progress tracking:** Resume interrupted migrations without losing work
- **Coordination:** Maintain consistency across related codebases

<Info>
The Codemod app provides centralized state management for large-scale refactoring across many repos. Pro codemods like [i18n codemod](https://codemod.com/i18n) use state to coordinate multi-repo migrations.
</Info>

```yaml workflow.yaml
version: "1"
state:
  schema:
    - name: shards
      type: array
      items:
        type: object
        properties:
          team:    { type: string }
          shardId: { type: string }
nodes: []
```

You can also declare state schema using an object map:

  ```yaml workflow.yaml
  version: "1"
  state:
    schema:
      shards:
        type: array
        items:
          type: object
          properties:
            team:    { type: string }
            shardId: { type: string }
  nodes: []
  ```

---

## Parameters

Parameters make workflows configurable and reusable across different projects and teams. Define them in your workflow with a schema:

```yaml workflow.yaml
version: "1"
params:
  schema:
    library:
      name: "Library"
      description: "Internationalization library to use"
      type: string
      default: "next-intl"
    shardingMethod:
      name: "Sharding Method"
      type: string
      default: "directory"
    prSize:
      name: "PR Size"
      type: string
      default: "50"
nodes:
  - id: transform
    steps:
      - name: Run transform
        js-ast-grep:
          js_file: ./src/codemod.ts
          language: tsx
```

### Accessing Parameters in Steps

Parameters are exposed differently depending on the step type:

**In jssg transforms** - access via `options.params`:

```ts codemod.ts
export default function transform(root: SgRoot<TSX>, options) {
  // Access parameters
  const library = options.params?.library || "next-intl";
  const catalogMethod = options.params?.catalogFileSelectionMethod;
  
  // Use parameters to drive behavior
  if (library === "react-i18next") {
    // Apply react-i18next specific logic
  }
}
```

<Info>
  For complete jssg parameter patterns and examples, see [jssg Advanced: Accessing Parameters](/jssg/advanced#accessing-parameters).
</Info>

**In shell steps** - parameters become environment variables with `PARAM_` prefix:

```yaml workflow.yaml
- name: Run script
  run: |
    echo "Library: $PARAM_LIBRARY"
```

**In codemod steps** - forwarded with `env_` prefix:

```yaml workflow.yaml
- name: Run nested codemod
  codemod:
    source: "@org/package"
  env:
    LIBRARY: ${{ params.library }}
# The nested workflow receives env_LIBRARY
```

**Common parameter use cases:**
- **Sharding configuration:** Method (`directory`, `codeowner`) and targets
- **PR settings:** Size limits, auto AI review, pre/post run scripts

<Info>
**Enterprise Features:** In the Codemod app, you can configure parameters visually through the UI. See [Campaigns](/migrations) for running parameterized workflows at scale.
</Info>

---

## Matrix Strategy

```yaml workflow.yaml
version: "1"
state:
  schema:
    - name: shards
      type: array
      items:
        type: object
        properties:
          team: { type: string }
          shardId: { type: string }
nodes:
  - id: matrix-codemod
    name: Matrix Codemod
    strategy:
      type: matrix
      from_state: shards
    steps:
      - name: Codemod
        run: node codemod.js --team=$team --shard=$shardId
```

<Accordion title="Dynamic Matrix Task Recompilation">
  When the array referenced by `from_state` changes, Codemod CLI:

  1. Creates new tasks for new items.
  2. Marks tasks as `WontDo` if their item is removed.
  3. Leaves existing tasks untouched if their item remains.

  <Info>
    Matrix nodes have a **master task** that tracks the status of all generated tasks.
  </Info>
</Accordion>

### Accessing Matrix Values in Steps

Matrix values are exposed differently per step type:

**In jssg transforms** - access via `options.matrixValues`:

```ts scripts/codemod.ts
const transform: Transform<TSX> = async (root, options) => {
  const team = options.matrixValues?.team;
  const shardId = options.matrixValues?.shardId;
  
  // Use for sharding logic
  if (team && !isOwnedByTeam(root.filename(), team)) {
    return null; // Skip files not owned by this team
  }
};
```

<Info>
  For complete jssg sharding patterns and examples, see [jssg Advanced: Accessing Matrix Values](/jssg/advanced#accessing-matrix-values).
</Info>

**In shell steps** - matrix values become environment variables:

```yaml workflow.yaml
- name: Process shard
  run: |
    echo "Team: $team"
    echo "Shard: $shardId"
```

---

## Manual Trigger

```yaml workflow.yaml
version: "1"
nodes:
  - id: manual-approval
    name: Manual Approval
    trigger:
      type: manual
    steps:
      - name: Wait for approval
        run: echo "Waiting for manual approval"
```

<Accordion title="Task UUIDs & Resume">
  Manual tasks are assigned unique UUIDs. You can resume:

  - All paused tasks:

    ```bash
    npx codemod workflow resume -i <run-id> --trigger-all
    ```
  - A specific task:

    ```bash
    npx codemod workflow resume -i <run-id> -t <task-uuid>
    ```
</Accordion>

---

## State Updates

| Syntax       | Meaning                            | Example                                 |
| ------------ | ---------------------------------- | --------------------------------------- |
| `KEY=VAL`    | Set state key to value             | `count=10`                              |
| `KEY@=VAL`   | Append value to array at state key | `shards@={"team":"core","shardId":"1"}` |
| Dot notation | Set nested state fields            | `config.retries=5`                      |
| JSON values  | Use valid JSON for objects/arrays  | `user={"name":"Alice","id":123}`        |

<Info>
  All state updates must be valid JSON if not a primitive. Updates are applied only if the task exits successfully.
</Info>

<AccordionGroup>
  <Accordion title="Container Runtimes">
    Planned feature: containerized execution (e.g., Docker/Podman). Currently, workflows run in the host shell. When available, you'll be able to specify a runtime per node or template.
  </Accordion>
  <Accordion title="State Management & Persistence">
    <Info>
      Workflow state is persisted after every task. If interrupted, you can resume from the last saved state—no work is lost.
    </Info>
  </Accordion>
  <Accordion title="Matrix Master Task">
    For matrix nodes, a master task aggregates the status of all generated tasks.\
    If all child tasks complete, the master is `Completed`. If any fail, the master is `Failed`.
  </Accordion>
  <Accordion title="Cyclic Dependency Example">
    If your workflow has a cycle:

    ```yaml workflow.yaml
    nodes:
      - id: a
        name: Task A
        depends_on: [b]
        steps:
          - name: Task A
            run: echo "Task A"
      - id: b
        name: Task B
        depends_on: [a]
        steps:
          - name: Task B
            run: echo "Task B"
    ```

    You'll see:

    ```bash console
    ✗ Workflow definition is invalid
    Error: Cyclic dependency detected: a → b → a
    ```

    <Info>
      This error is shown when you run `npx codemod workflow validate` or `npx codemod workflow run` on a workflow with a cyclic dependency.
    </Info>
  </Accordion>
</AccordionGroup>

---

## Task Statuses

<ResponseField name="Pending">
  Queued; waiting for runner.
</ResponseField>

<ResponseField name="Running">
  Currently executing.
</ResponseField>

<ResponseField name="Completed">
  Succeeded; diff applied.
</ResponseField>

<ResponseField name="Failed">
  Script exited non-zero; diff discarded.
</ResponseField>

<ResponseField name="AwaitingTrigger">
  Waiting for manual approval.
</ResponseField>

<ResponseField name="Blocked">
  Dependencies not finished.
</ResponseField>

<ResponseField name="WontDo">
  Matrix item removed; task skipped.
</ResponseField>

## Variable Resolution

- **Parameter:** `${{params.branch}}` — Supplied at runtime
- **Environment:** `${{env.CI}}` — Host env var
- **Shared State:** `${{state.counter}}` — Live JSON value

<Tip>
  - In matrix tasks, each object key becomes an environment variable (e.g., `$team`, `$shardId`, …). Inside steps, variables are unprefixed.

  - Operators (`==`, `!=`, `>`, `<`, `&&`, `||`) are supported inside string interpolations like `${{ ... }}` for resolving parameters, state, and matrix values.
</Tip>

---

## Roadmap

<Steps>
  <Step title="Container runtime support" icon="flag-pennant">
    Support for `runtime: docker` and other container runtimes, allowing tasks to run in isolated environments.
  </Step>
  <Step title="Nested matrix strategies" icon="flag-pennant">
    Support for matrix strategies within matrix strategies, enabling more complex task fan-out.
  </Step>
</Steps>

## Next Steps

<CardGroup cols={2}>
  <Card title="Codemod CLI Reference" icon="terminal" href="/cli/cli-reference">
    Explore the full command and option reference for Codemod CLI.
  </Card>
  <Card title="Build with Codemod MCP" icon="robot" href="/model-context-protocol">
    Learn how to build Codemod packages in your IDE using Codemod MCP.
  </Card>
</CardGroup>

